/* ----------------------------------------------------------------------------
 * The materials provided here are for sample use and are provided "as-is";
 * Oracle disclaims all express and implied warranties, including, the implied
 * warranties of merchantability or fitness for a particular use. Oracle shall
 * not be liable for any damages, including, direct, indirect, incidental,
 * special or consequential damages for loss of profits, revenue, data or data
 * use, incurred by you or any third party in connection with the use of these
 * materials.
 *
 * DESCRIPTION  The class that launches internet connection using CE's ConnectionManager
 * ---------------------------------------------------------------------- */
#include "ConnManager.h"
#include <initguid.h>           // help in defining IID_ConnPrv_IProxyExtension
#include <connmgr_proxy.h>      // PROXY_CONFIG
#include <wininet.h>
#pragma comment(lib, "cellcore.lib")


#ifndef EXIT_ON_FAILED
#define EXIT_ON_FAILED(_hr)\
    if (FAILED(_hr))\
    {   \
        RETAILMSG(1,(_T("CEMAPI: FAILED(%x)at %hs:%d\n"), \
            _hr, __FILE__, __LINE__)); \
        goto FuncExit; \
    }
#endif //EXIT_ON_FAILED

#ifndef RELEASE_OBJ
#define RELEASE_OBJ(s)\
    if (s != NULL)\
    {                   \
        s->Release();   \
        s = NULL;       \
    }
#endif //RELEASE_OBJ

CFreeObject<CConnManager> CConnManager::m_pInstance = NULL;
const DWORD gc_dwMaxWaitInterval = 12000;


// **************************************************************************
// Function Name: CConnManager
//
// Purpose: Initialize the CConnManager object
// Arguments:	none
// Return Values:	none
// Side effects:
// Description: CConnManager Constructor
CConnManager::CConnManager()
{}

// **************************************************************************
// Function Name: ~CConnManager
//
// Purpose: Cleans up CConnManager object
// Arguments:
// Return Values:
// Side effects:
// Description:
//	Destructor for CConnManager object
CConnManager::~CConnManager()
{}


// get the singleton
CConnManager* CConnManager::GetInstance()
{
	if ( NULL == m_pInstance )
	{
		m_pInstance = new CConnManager;
	}

	return m_pInstance;
}

// Establish a Connection
HRESULT CConnManager::ConnectWithConnMgr(const GUID* pGuid, HANDLE* phConnHandle)
{
	HRESULT                 hr              = S_OK;
	HANDLE                  hConnHandle     = NULL;
	DWORD                   dwWaitStartTick = 0;
	DWORD                   dwResult        = 0;
	DWORD                   dwStatus        = 0;
	LONG                    lWaitInterval   = gc_dwMaxWaitInterval;
	BOOL                    bContinueWait   = TRUE;
	CONNMGR_CONNECTIONINFO  ConnInfo        = {0};

	// Register with the connection manager
	ConnInfo.cbSize			= sizeof(CONNMGR_CONNECTIONINFO);
	//	We want to use the "guidDestNet" parameter
	ConnInfo.dwParams		= CONNMGR_PARAM_GUIDDESTNET;
	//	This is the highest data priority.
	ConnInfo.dwPriority		= CONNMGR_PRIORITY_USERINTERACTIVE;
	ConnInfo.dwFlags		= CONNMGR_FLAG_PROXY_WAP;
	//	Lets be nice and share the connection with other applications.
	ConnInfo.bExclusive		= FALSE;
	ConnInfo.bDisabled		= FALSE;
	ConnInfo.guidDestNet	= *pGuid;

	// Try to connect to this network.
	hr = ::ConnMgrEstablishConnection(&ConnInfo, &hConnHandle);
	EXIT_ON_FAILED(hr);

	// Set the timeout
	dwWaitStartTick = ::GetTickCount();

	// Wait for the connection.
	while ( lWaitInterval > 0 && bContinueWait )
	{
		// Wait for some status from connmgr.
		dwResult = ::WaitForSingleObject(hConnHandle, lWaitInterval);

		if ( WAIT_OBJECT_0 == dwResult )
		{
			hr = ::ConnMgrConnectionStatus(hConnHandle, &dwStatus);
			EXIT_ON_FAILED(hr);

			switch ( dwStatus )
			{
			case CONNMGR_STATUS_CONNECTED:
			case CONNMGR_STATUS_NOPATHTODESTINATION:
			case CONNMGR_STATUS_WAITINGFORPATH:
			case CONNMGR_STATUS_CONNECTIONFAILED:
			case CONNMGR_STATUS_CONNECTIONCANCELED:
			case CONNMGR_STATUS_UNKNOWN:
			case CONNMGR_STATUS_CONNECTIONDISABLED:
			case CONNMGR_STATUS_WAITINGFORNETWORK:
				bContinueWait = FALSE;
				break;

			default:
				break;
			}
		}
		else if ( WAIT_FAILED == dwResult )
		{
			// Error during wait.
			hr = HRESULT_FROM_WIN32(::GetLastError());
			EXIT_ON_FAILED(hr);
		}
		else if ( WAIT_TIMEOUT == dwResult )
		{
			// Waiting timed out.
			hr = E_FAIL;
			EXIT_ON_FAILED(hr);
		}

		// Calculate the remaining timeout value.
		// Subtract the time that's already elapsed.
		// Time is in milliseconds.
		lWaitInterval = gc_dwMaxWaitInterval - (::GetTickCount() - dwWaitStartTick);
	}

	if ( CONNMGR_STATUS_CONNECTED != dwStatus )
	{
		TCHAR dwS[80];
		::wsprintf(dwS, TEXT("dwStatus: %d"), dwStatus);

		hr = E_FAIL;
		EXIT_ON_FAILED(hr);
	}

	// Handle off connection handle
	*phConnHandle = hConnHandle;
	hConnHandle = NULL;

FuncExit:
	if ( NULL != hConnHandle )
	{
		::ConnMgrReleaseConnection(hConnHandle, TRUE);
	}

	return hr;
}

// Release the Connection
HRESULT CConnManager::ReleaseConnection(HANDLE hConnHandle)
{
	return ConnMgrReleaseConnection(hConnHandle, TRUE);
}
